// 本库专用于对UTS库的功能进行二次封装
// 1、可使代码提示有效
// 2、可使类型约束有效
// 3、可避免UTS插件强类型要求导致的闪退风险
// 作者：DXL
// 日期：20240522

// 导入原生平台实现的UTS插件
import * as UTSBLE from '@/uni_modules/xl-uts-bluetooth';


/**
 * 一个用于保存参数的映射表，key为接口名称 + 参数id，值为参数的回调的存放对象
 */
const optionsMap = {};
/**
 * 一个用于保存参数的存活周期的映射表，key为接口名称 + 参数id，值为参数的回调的存放对象的存活周期，是时间戳值
 * 我们规定，API的调用是短暂而迅速的，是异步的，是在3s内能迅速进行上报complete的，如果超过3s未上报，则认为此次API请求出现耗时异常
 * 如果超过5s未执行完毕，则认为此次API调用完全脱离掌控，出现未回复异常，此时我们需要回收相关的资源，清除 optionsMap 注册的信息
 * 提示：这个功能主要是解决有些蓝牙通信的API在断开链接后可能不会继续回调，导致 optionsMap 不清除已注册的资源的问题
 */
const optionsLife = {};


/**
 * 封装的获取接口调用参数的唯一key的函数
 * @param {String} apiName 接口名称
 * @param {Object} optionsId 接口缓存回调参数时的id
 */
function getOptionsKeyFromId(apiName, optionsId) {
    return `${apiName}-${optionsId}`;
}

/**
 * 在Android下，有一些字段是多余的，调试的时候打印出来浪费性能+影响观感
 * 因此我们可以屏蔽掉
 */
function filterAndroidErrorField(callback) {
    // 如果未实现这个回调函数，我们也没必要注入
    if (callback == undefined || callback == null) {
        return undefined;
    }
    return (err) => {
        // 从error信息中删除掉我们不需要的字段，目前仅仅Android端JS引擎的UNIAPP会有这些字段返回
        delete err['stackTrace'];
        delete err['name'];
        delete err['message'];
        delete err['suppressedExceptions'];
        // 然后我们再调用实际的外部的处理函数回传结果
        callback(err);
    }
}

/**
 * 在iOS下，有一些字段是多余的，调试的时候打印出来浪费性能+影响观感
 * 因此我们可以屏蔽掉
 */
function filterIOSErrorField(callback) {
    // 如果未实现这个回调函数，我们也没必要注入
    if (callback == undefined || callback == null) {
        return undefined;
    }
    return (err) => {
        // 从error信息中删除掉我们不需要的字段，目前仅仅iOS端JS引擎的UNIAPP会有这些字段返回
        delete err['userInfo'];
        delete err['name'];
        delete err['message'];
        delete err['domain'];
        delete err['code'];
        // 然后我们再调用实际的外部的处理函数回传结果
        callback(err);
    }
}



// see if it looks and smells like an iterable object, and do accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          typeof (item.length) === "number" && 
          (item.length === 0 ||
             (item.length > 0 && 
             (item.length - 1) in item)
          )
        )
    );
}

/**
 * 封装的过滤所有接口请求的参数的函数，对参数进行某些比如类型修正
 * 理论上，我们不应该对接口的参数进行自动修正，而是应该根据接口的不同参数类型，手动对接口进行一一对应的
 * 1、类型修正
 * 2、可选值修正
 * 
 * 但是目前我们没什么时间去做这个一一修正的逻辑，因此此处先自动修正arraybuffer和uint8array的问题
 * TODO 后面有空了再实现上述的一一修正的逻辑，把非可选值缺失这个检查逻辑也给加上
 * 
 * @param {Object} options 接口的参数
 */
function filterApiOptions(options) {
    let optionsKeys = Object.keys(options);
    for (let i = 0; i < optionsKeys.length; i++) {
        let key = optionsKeys[i];   // 参数的key名
        let optionsValue = options[key];    // 参数的值
        
        // --- 在下方代码中，进行一些参数的操作 ---
        
        // 1、类型修正，将可能为Uint8Array或者ArrayBuffer的字节数组类型，修正为Array<number>
        if (optionsValue instanceof ArrayBuffer) {  // arraybuffer并不是arraylike，我们需要进行单独修正
            optionsValue = new Uint8Array(optionsValue);
            optionsValue = Array.from(optionsValue);
            // console.warn("[BLE.js] UTS插件接口暂时不支持 ArrayBuffer 类型，自动修正为 Array<T>类型！");
        } else if (isArrayLike(optionsValue)) {    // 修正之后的类型如果是array类似的类型，那我们就需要将其转换为 Array<T> 对象
            optionsValue = Array.from(optionsValue);
            // console.warn("[BLE.js] UTS插件接口暂时不支持非 Array<T> 类型，自动修正为 Array<T>类型！");
        }
        
        // 最终，把可能更新的值，重新set回参数对象中
        options[key] = optionsValue;
    }
}

/**
 * 根据当前传入的参数对象，判断是否是无回调执行方式
 * 
 * @param {Object} options 参数对象
 */
function isNoCallbackInvoke(options) {
    // 三个都不存在才认为是需要无回调式调用
    if (options.success == undefined && 
        options.fail == undefined && 
        options.complete == undefined) {
        return true;
    }
    return false;
}

/**
 * 这个接口存在的意义就是拦截所有的接口的 options 里面的所有回调，因为现在UTS插件在uniapp js引擎上有些问题
 * 这个问题会导致js调用任何的uts接口，只要传入了callback，那么这个callback就创建一个对象在UTS层被缓存起来，
 * 而且目前无任何释放的可能性，这就导致了，如果多次调用接口，频繁调用接口，就一定会堆积内存的消耗，最终导致撑炸APP导致闪退
 * 目前此问题暂时无解，只能通过一些方法取巧解决，也就是尽量不要调用原生接口的时候传callback。那接口如果想要在尽可能少改动的情况下
 * 实现替补方案，就只有在js层缓存callback，然后uts层通过一个统一个回调去通知结果
 * 
 * 注：目前仅在iOS端有这个问题，我们需要检查仅在iOS平台才进行此补丁
 * 
 * @param {String} apiName 接口名称
 * @param {Object} options 调用API的参数
 */
function proxyApiOptions(apiName, options) {
    let platform = uni.getSystemInfoSync().platform;
    if (platform == 'ios') {
        // 生成一个随机ID，用于映射此参数的回调函数
        let optionsId = parseInt(Math.random() * 10000000000);
        let optionsKey = getOptionsKeyFromId(apiName, optionsId);
        // 参数过滤，修正
        filterApiOptions(options);
        // 如果是无回调式调用，我们就需要创建一个Promise对象返回给api调用者
        if (isNoCallbackInvoke(options)) {
            return new Promise((resolve, reject) => {
                optionsMap[optionsKey] = {
                    success: resolve,
                    fail: filterIOSErrorField(reject),
                };
                optionsLife[optionsKey] = new Date().getTime();
                UTSBLE[`${apiName}UTSCallbackWorkaround`](options, optionsId);
            });
        } else {
            // 我们需要拿到外部调用时的callback引用，在js层缓存
            let cbk_success = options.success;
            let cbk_fail = options.fail;
            let cbk_complete = options.complete;
            // 缓存到全局域
            optionsMap[optionsKey] = {
                success: cbk_success,
                fail: filterIOSErrorField(cbk_fail),
                complete: filterIOSErrorField(cbk_complete),
            };
            // 从options里面删掉这个引用，避免传到UTS层导致内存泄漏
            delete options.success;
            delete options.fail;
            delete options.complete;
            // 缓存本次API的开始时间
            optionsLife[optionsKey] = new Date().getTime();
            // 调用API的时候，使用专门的封装后缀的API，API的名称格式规范为 writeBLECharacteristicValueUTSCallbackWorkaround
            // 也是就是说，插件开发中底层必须要实现这个UTS函数，不然此补丁函数无法运行
            UTSBLE[`${apiName}UTSCallbackWorkaround`](options, optionsId);
        }
    } else if (platform == 'android') {
        // 参数过滤，修正
        filterApiOptions(options);
        // 如果是无回调式调用，我们就需要创建一个Promise对象返回给api调用者
        if (isNoCallbackInvoke(options)) {
            return new Promise((resolve, reject) => {
                options.success = resolve;
                // 调用链：原生层调用options.fail -> filterAndroidErrorField内部返回的代理函数 -> 最终调用reject
                options.fail = filterAndroidErrorField(reject);
                UTSBLE[apiName](options);
            });
        } else {
            // android下暂时没发现问题可直接调用API，顺便我们可以过滤错误日志的一些字段
            options.fail = filterAndroidErrorField(options.fail);
            options.complete = filterAndroidErrorField(options.complete);
            UTSBLE[apiName](options);
        }
    } else {
        console.error("本插件不支持的平台：" + platform);
    }
}

/**
 * 此回调函数会静态注册到UTS插件库中，在iOS平台js引擎下，所有的API调用都会走这个函数进行结果上报
 * 
 * @param {string} apiName 触发事件的接口的名称，不带 UTSCallbackWorkaround 结尾
 * @param {number} optionsId 触发事件的接口的执行Id，
 *  每一次执行都有对应的类似seseionId用于标志result事件是由是哪一次调用api的触发的
 * @param {'success' | 'fail' | 'complete'} event 触发的事件的类型
 * @param {object} res 触发的事件的结果资源，如果是success那就是res，如果是fail那就是err，如果是complete那两者都有可能 
 */
function onApiResultUpdate(apiName, optionsId, event, res) {
    // 拼装我们规定格式的key，用来索引对用请求的callback信息
    let optionsKey = getOptionsKeyFromId(apiName, optionsId);
    let callbackFn = optionsMap[optionsKey][event];
    // 如果是complete事件，就把这个缓存删除掉释放内存，complete执行就意味着接口彻底执行完毕了
    if (event == 'complete') {
        delete optionsLife[optionsKey];
        delete optionsMap[optionsKey];
        // console.log("回收 " + optionsKey + " 的资源完成");
    }
    // 执行映射表里面的回调函数，参数就是此次事件触发时，回传的数据
    if (callbackFn != undefined) {
        callbackFn(res);    // 可能外部调用者并没有实现此回调函数，我们按需进行执行
    }
}

// 只有iOS下有这个问题目前发现，因此这个补丁仅仅需要打在iOS下
if (uni.getSystemInfoSync().platform == 'ios') {
    // 补丁的核心，全局注册一个监听函数，在UTS层的API出现结果后，通过此监听函数转交接口的相关信息和事件类型
    UTSBLE.oniOSApiResult(onApiResultUpdate);
    
    // 注意，在安装APP后调用 openBluetoothAdapter 可能会有首次弹窗请求BLE的权限的问题，此时必须要等到用户授权或者拒绝授权后
    // 结果才会更新，而且经过测试，在进行此类权限申请的操作的时候，APP页面在系统的优先级中是最高的
    // 也就是说，暂时不用担心 openBluetoothAdapter 接口不走回调的问题，因此我们需要在此处做一个处理，遇到某一些一定有结果的接口，就不去检查
    const whitelist = [
        'openBluetoothAdapter',
    ];
    
    /**
     * 检查某个API是否在补丁机制的白名单中
     * @param {string} key API请求的参数的key
     */
    function isApiInWitelist(key) {
        for (let i = 0; i < whitelist.length; i++) {
            if (key.startsWith(whitelist[i])) {
                return true;
            }
        }
        return false;
    }
    
    // 全局仅需启动一次检查，且只需1s检查一次
    setInterval(() => {
        let keys = Object.keys(optionsLife);
        keys.forEach((key) => {
            if (isApiInWitelist(key)) {
                return; // 在白名单中的API，可以直接忽略
            }
            let timestamp = optionsLife[key];
            let currentTime = new Date().getTime();
            if (currentTime - timestamp > 5000) {
                delete optionsLife[key];
                delete optionsMap[key];
                console.info("[BLE.js] 已经回收超过5s的API请求记录：" + key);
            } // else
            // if (currentTime - timestamp > 3000) {
            //     console.info("[BLE.js] 发现存在超过3s的API请求记录，在5s后，该请求将被强行结束：" + key);
            // }
        });
    }, 1000);
}

// ------------------------ 接口通用JSDOC ------------------------

/**
 * @callback ApiFail
 * @param {UniError} err - The error object containing details of the failure.
 */

/**
 * @callback ApiComplete
 * @param {object} res - The result object from the API call.
 */

/**
 * @typedef {Object} ApiCommonResult
 * @property {number} errCode - The error code returned by the API.
 * @property {string} errMsg - The error message returned by the API.
 */

/**
 * @callback ApiSuccess
 * @param {ApiCommonResult} res - The result object with error code and message.
 */

// ### 接口中通用数据类型封装的JSDOC

/**
 * 定义蓝牙设备的数据结构。
 * 
 * @typedef {Object} BlueToothDevice
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {string} name - 蓝牙设备的名称。
 * @property {string} localName - 蓝牙设备的本地名称。
 * @property {number} RSSI - 接收信号强度指示（Received Signal Strength Indicator）。
 * @property {number[]} advertisData - 广播数据，通常为一串数字数组。
 * @property {string[]} advertisServiceUUIDs - 此设备广播的service UUID列表。
 * @property {Map<string, number[]>|undefined} [serviceData] - 服务数据映射，键为service UUID，值为数据数组。
 * @property {boolean|null} connectable - 设备是否可连接，可以是布尔值或null。
 */

/**
 * 定义蓝牙设备服务的数据结构。
 * 
 * @typedef {Object} BLEService
 * 
 * @property {string} uuid - 服务的通用唯一识别码(UUID)。
 * @property {boolean} isPrimary - 标识该服务是否是主要服务。
 */

/**
 * 定义蓝牙设备特征的数据结构。
 * 
 * @typedef {Object} BLECharacteristic
 * 
 * @property {string} uuid - 特征的UUID。
 * @property {CharacteristicProperties} properties - 特征支持的属性集合。
 */

/**
 * 定义蓝牙设备特征属性的数据结构。
 * 
 * @typedef {Object} CharacteristicProperties
 * 
 * @property {boolean} read - 是否支持读取。
 * @property {boolean} write - 是否支持写入。
 * @property {boolean} notify - 是否支持通知。
 * @property {boolean} indicate - 是否支持指示。
 * @property {boolean} writeNoResponse - 是否支持无响应写入。
 * @property {boolean} writeDefault - 是否有默认写入行为。
 */

/**
 * 用于描述与Characteristic相关的操作及其结果通知的核心信息结构。
 * 
 * @typedef {Object} CharacteristicOptions
 * 
 * @property {string} deviceId - 设备的ID。
 * @property {string} service - 服务的UUID。
 * @property {string} characteristic - 特征的UUID。
 */

// ### 接口中通用数据类型封装的JSDOC

/**
 * 配置选项以打开蓝牙适配器。
 * 
 * @typedef {Object} OpenBluetoothAdapterOptions
 * 
 * @property {('central'|'peripheral')} [mode] - 蓝牙工作模式，设备可作为中心或外围设备。此设置仅iOS设备需要。
 *   - `'central'`: 表示主机模式。
   - `'peripheral'`: 表示从机（外设）模式。
   * 要求基础库版本： `2.10.0` 及以上。
 * 
 * @property {ApiSuccess=} [success] - 操作成功的回调函数。
 * 
 * @property {ApiFail=} [fail] - 操作失败的回调函数。
 * 
 * @property {ApiComplete=} [complete] - 操作完成的回调函数（不论成功与否）。
 */

/**
 * 配置选项以关闭蓝牙适配器。
 * 
 * @typedef {Object} CloseBluetoothAdapterOptions
 * 
 * @property {ApiSuccess=} [success] - 操作成功时的回调函数。
 * 
 * @property {ApiFail=} [fail] - 操作失败时的回调函数。
 * 
 * @property {ApiComplete=} [complete] - 操作完成时的回调函数（无论成功还是失败）。
 */

/**
 * 成功获取蓝牙适配器状态的回调结果结构。
 * 
 * @typedef {Object} GetBluetoothAdapterStateSuccessCallbackResult
 * 
 * @property {boolean} discovering - 表示当前是否正在搜索蓝牙设备。
 * @property {boolean} available - 指示蓝牙适配器是否可用。
 * @property {string} errMsg - 错误信息，如果操作成功，通常是一个空字符串或成功提示信息。
 */

/**
 * 获取蓝牙适配器状态的配置选项。
 * 
 * @typedef {Object} GetBluetoothAdapterStateOptions
 * 
 * @property {(res: GetBluetoothAdapterStateSuccessCallbackResult) => void} [success] - 获取状态成功时的回调函数，接收一个状态结果对象作为参数。
 * @property {ApiFail=} [fail] - 操作失败时的回调函数。
 * @property {ApiComplete=} [complete] - 操作完成时的回调函数（无论成功或失败）。
 */

/**
 * 蓝牙适配器状态变化监听器的回调结果类型。
 * 
 * @typedef {Object} OnBluetoothAdapterStateChangeListenerResult
 * 
 * @property {boolean} available - 指示蓝牙适配器是否可用。
 * @property {boolean} discovering - 表明蓝牙适配器是否正处于搜索设备的状态。
 */

/**
 * 蓝牙适配器状态改变时的回调函数类型。
 * 
 * @callback OnBluetoothAdapterStateChangeCallback
 * 
 * @param {OnBluetoothAdapterStateChangeListenerResult} result - 蓝牙适配器状态变化的具体信息。
 */

/**
 * 开始蓝牙设备发现过程的配置选项。
 * 
 * @typedef {Object} StartBluetoothDevicesDiscoveryOptions
 * 
 * @property {string[]} [services] - 要搜索的蓝牙设备主服务的 UUID 列表，支持 16/32/128 位 UUID。通过指定这些 UUID，可以过滤掉不需要处理的其他蓝牙设备，仅搜索广播包中包含对应 UUID 的设备。
 * @property {boolean} [allowDuplicatesKey=false] - 是否允许重复上报同一设备。如果设置为`true`，则`onBluetoothDeviceFound`回调可能会多次接收到同一个设备的信息，但RSSI值可能不同。
 * @property {number} [interval=0] - 上报新发现设备的间隔时间（单位：毫秒）。设为0表示立即上报新设备；其他值则按照设定的间隔时间上报。
 * @property {'low'|'medium'|'high'} [powerLevel] - 扫描模式级别，级别越高扫描速度越快，同时更耗电。
   - `'low'`: 低功率模式;
   - `'medium'`: 中等功率模式;
   - `'high'`: 高功率模式;
 * @property {ApiSuccess=} [success] - 发现设备开始成功时的回调函数。
 * @property {ApiFail=} [fail] - 发现设备开始失败时的回调函数。
 * @property {ApiComplete=} [complete] - 发现设备开始操作完成的回调函数（无论成功或失败）。
 */

/**
 * 获取已发现蓝牙设备列表的成功回调结果类型。
 * 
 * @typedef {Object} GetBluetoothDevicesSuccessCallbackResult
 * 
 * @property {BlueToothDevice[]} devices - 已发现的蓝牙设备列表。
 * @property {string} errMsg - 错误信息，如果操作成功，通常为空字符串或成功提示。
 */

/**
 * 获取已发现蓝牙设备列表的配置选项。
 * 
 * @typedef {Object} GetBluetoothDevicesOptions
 * 
 * @property {(res: GetBluetoothDevicesSuccessCallbackResult) => void} [success] - 操作成功时的回调函数，返回设备列表及错误信息。
 * @property {ApiFail=} [fail] - 操作失败时的回调函数。
 * @property {ApiComplete=} [complete] - 操作完成时的回调函数（无论成功或失败）。
 */

/**
 * 停止蓝牙设备发现过程的配置选项。
 * 
 * @typedef {Object} StopBluetoothDevicesDiscoveryOptions
 * 
 * @property {(res: ApiCommonResult) => void} [success] - 成功停止设备发现时的回调函数，接收一个通用结果对象作为参数。
 * @property {ApiFail=} [fail] - 发现停止失败时的回调函数。
 * @property {ApiComplete=} [complete] - 发现停止操作完成的回调函数（无论成功或失败）。
 */

/**
 * 蓝牙低功耗（BLE）连接状态变化监听器的回调结果类型。
 * 
 * @typedef {Object} OnBLEConnectionStateChangeListenerResult
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {boolean} connected - 指示设备当前是否处于已连接状态。
 */

/**
 * 蓝牙低功耗（BLE）连接状态改变时的回调函数类型。
 * 
 * @callback OnBLEConnectionStateChangeCallback
 * 
 * @param {OnBLEConnectionStateChangeListenerResult} res - 包含连接状态变化详情的对象。
 */

/**
 * 创建蓝牙低功耗（BLE）设备连接的配置选项。
 * 
 * @typedef {Object} CreateBLEConnectionOptions
 * 
 * @property {string} deviceId - 目标蓝牙设备的唯一标识符。
 * @property {number} [timeout] - 连接超时时间，单位为毫秒。如果不指定，则表示连接操作不会因超时而失败。
 * @property {(res: ApiCommonResult) => void} [success] - 连接成功时的回调函数，接收一个通用结果对象作为参数。
 * @property {ApiFail=} [fail] - 连接失败时的回调函数。
 * @property {ApiComplete=} [complete] - 连接操作完成的回调函数（无论成功或失败）。
 */

/**
 * 关闭与蓝牙低功耗（BLE）设备连接的配置选项。
 * 
 * @typedef {Object} CloseBLEConnectionOptions
 * 
 * @property {string} deviceId - 要断开连接的蓝牙设备的唯一标识符。
 * @property {ApiSuccess=} [success] - 断开连接成功时的回调函数。
 * @property {ApiFail=} [fail] - 断开连接失败时的回调函数。
 * @property {ApiComplete=} [complete] - 断开连接操作完成的回调函数（无论成功或失败）。
 */

/**
 * 蓝牙低功耗（BLE）最大传输单元（MTU）变化监听器的回调结果类型。
 * 
 * @typedef {Object} OnBLEMTUChangeListenerResult
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {number} mtu - 更新后的最大传输单元值。
 */

/**
 * 蓝牙低功耗（BLE）设备最大传输单元（MTU）改变时的回调函数类型。
 * 
 * @callback OnBLEMTUChangeCallback
 * 
 * @param {OnBLEMTUChangeListenerResult} result - 包含MTU变化详情的对象。
 */

/**
 * 设置蓝牙低功耗（BLE）设备最大传输单元（MTU）的配置选项。
 * 
 * @typedef {Object} SetBLEMTUOptions
 * 
 * @property {string} deviceId - 目标蓝牙设备的唯一标识符。
 * @property {number} mtu - 要设置的最大传输单元值。
 * @property {(res: ApiCommonResult) => void} [success] - 设置MTU成功时的回调函数，接收一个通用结果对象作为参数。
 * @property {ApiFail=} [fail] - 设置MTU失败时的回调函数。
 * @property {ApiComplete=} [complete] - 设置MTU操作完成的回调函数（无论成功或失败）。
 */

/**
 * 获取蓝牙低功耗（BLE）设备最大传输单元（MTU）成功的回调结果类型。
 * 
 * @typedef {Object} GetBLEMTUSuccessCallbackResult
 * 
 * @property {number} mtu - 当前的最大传输单元值。
 * @property {string} errMsg - 错误信息，如果操作成功，通常为空字符串或成功提示。
 */

/**
 * 获取蓝牙低功耗（BLE）设备最大传输单元（MTU）的配置选项。
 * 
 * @typedef {Object} GetBLEMTUOptions
 * 
 * @property {string} deviceId - 目标蓝牙设备的唯一标识符。
 * @property {'write'|'writeNoResponse'} [writeType] - 写入类型，可选，默认为标准写入。
 * @property {(res: GetBLEMTUSuccessCallbackResult) => void} [success] - 获取MTU成功时的回调函数，返回MTU值及错误信息。
 * @property {ApiFail=} [fail] - 获取MTU失败时的回调函数。
 * @property {ApiComplete=} [complete] - 获取MTU操作完成的回调函数（无论成功或失败）。
 */

/**
 * 获取蓝牙低功耗（BLE）设备服务成功的回调结果类型。
 * 
 * @typedef {Object} GetBLEDeviceServicesSuccessCallbackResult
 * 
 * @property {string} errMsg - 错误信息，如果操作成功，通常为空字符串或成功提示。
 * @property {BLEService[]} services - 设备上找到的服务列表。
 */

/**
 * 获取蓝牙低功耗（BLE）设备服务的配置选项。
 * 
 * @typedef {Object} GetBLEDeviceServicesOptions
 * 
 * @property {string} deviceId - 目标蓝牙设备的唯一标识符。
 * @property {(res: GetBLEDeviceServicesSuccessCallbackResult) => void} [success] - 获取服务成功时的回调函数，返回服务列表及错误信息。
 * @property {ApiFail=} [fail] - 获取服务失败时的回调函数。
 * @property {ApiComplete=} [complete] - 获取服务操作完成的回调函数（无论成功或失败）。
 */

/**
 * 获取蓝牙低功耗（BLE）设备指定服务下的特征值成功的回调结果类型。
 * 
 * @typedef {Object} GetBLEDeviceCharacteristicsSuccessCallbackResult
 * 
 * @property {BLECharacteristic[]} characteristics - 特征值列表。
 * @property {string} errMsg - 错误信息，如果操作成功，通常为空字符串或成功提示。
 */

/**
 * 获取蓝牙低功耗（BLE）设备指定服务下特征值的配置选项。
 * 
 * @typedef {Object} GetBLEDeviceCharacteristicsOptions
 * 
 * @property {string} deviceId - 目标蓝牙设备的唯一标识符。
 * @property {string} serviceId - 要查询的服务的ID。
 * @property {(res: GetBLEDeviceCharacteristicsSuccessCallbackResult) => void} [success] - 获取特征值成功时的回调函数，返回特征值列表及错误信息。
 * @property {ApiFail=} [fail] - 获取特征值失败时的回调函数。
 * @property {ApiComplete=} [complete] - 获取特征值操作完成的回调函数（无论成功或失败）。
 */

/**
 * 读取蓝牙低功耗（BLE）设备指定特征值的配置选项。
 * 
 * @typedef {Object} ReadBLECharacteristicValueOptions
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {string} serviceId - 特征值所属服务的ID。
 * @property {string} characteristicId - 要读取的特征值的ID。
 * @property {ApiSuccess=} [success] - 读取成功时的回调函数。
 * @property {ApiFail=} [fail] - 读取失败时的回调函数。
 * @property {ApiComplete=} [complete] - 读取操作完成的回调函数（无论成功或失败）。
 */

/**
 * 写入蓝牙低功耗（BLE）设备指定特征值的配置选项。
 * 
 * @typedef {Object} WriteBLECharacteristicValueOptions
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {string} serviceId - 特征值所属服务的ID。
 * @property {string} characteristicId - 要写入的特征值的ID。
 * @property {number[]} value - 待写入的特征值数据，为数字数组。
 * @property {'write'|'writeNoResponse'} [writeType] - 写入类型，默认为 `'write'`，可选 `'writeNoResponse'` 以减少交互延迟但不保证写入成功。
 * @property {ApiSuccess=} [success] - 写入成功时的回调函数。
 * @property {ApiFail=} [fail] - 写入失败时的回调函数。
 * @property {ApiComplete=} [complete] - 写入操作完成的回调函数（无论成功或失败）。
 */

/**
 * 批量写入蓝牙低功耗（BLE）设备指定特征值的配置选项。
 * 相较于`WriteBLECharacteristicValueOptions`，增加了对每次分包发送大小的控制。
 * 
 * @typedef {Object} BatchWriteBLECharacteristicValueOptions
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {string} serviceId - 特征值所属服务的ID。
 * @property {string} characteristicId - 要写入的特征值的ID。
 * @property {number[]} value - 待写入的特征值数据数组。
 * @property {number} size - 每次分包发送的大小。
 * @property {'write'|'writeNoResponse'} [writeType] - 写入类型，默认为 `'write'`，可选 `'writeNoResponse'` 以减少交互延迟，但不保证写入成功。
 * @property {ApiSuccess=} [success] - 写入成功时的回调函数。
 * @property {ApiFail=} [fail] - 写入失败时的回调函数。
 * @property {ApiComplete=} [complete] - 写入操作完成的回调函数（无论成功或失败）。
 */

/**
 * 设置蓝牙低功耗（BLE）设备特征值通知状态的配置选项。
 * 
 * @typedef {Object} NotifyBLECharacteristicValueChangeOptions
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {string} serviceId - 特征值所属服务的ID。
 * @property {string} characteristicId - 特征值的ID。
 * @property {boolean} state - 通知状态，`true` 表示开启通知或指示，`false` 表示关闭。
 * @property {'notification'|'indication'} [type='notification'] - 通知或指示类型，默认为 `'notification'`，可选 `'indication'`。
 * @property {ApiSuccess=} [success] - 设置成功时的回调函数。
 * @property {ApiFail=} [fail] - 设置失败时的回调函数。
 * @property {ApiComplete=} [complete] - 设置操作完成的回调函数（无论成功或失败）。
 */

/**
 * 蓝牙低功耗（BLE）特征值变化监听器的回调结果类型。
 * 
 * @typedef {Object} OnBLECharacteristicValueChangeListenerResult
 * 
 * @property {string} deviceId - 蓝牙设备的唯一标识符。
 * @property {string} serviceId - 变化特征值所属服务的ID。
 * @property {string} characteristicId - 变化的特征值的ID。
 * @property {number[]} value - 特征值的最新数据，为数字数组。
 */

/**
 * 蓝牙低功耗（BLE）特征值改变时的回调函数类型。
 * 
 * @callback OnBLECharacteristicValueChangeCallback
 * 
 * @param {OnBLECharacteristicValueChangeListenerResult} res - 包含特征值变化详情的对象。
 */

// ------------------------ 接口通用JSDOC ------------------------

// ------------------------ 二次封装的接口定义 ------------------------

/**
 * 异步打开蓝牙适配器的方法。
 * 
 * @param {OpenBluetoothAdapterOptions} options - 打开蓝牙适配器的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，操作的结果通过在`OpenBluetoothAdapterOptions`中指定的回调函数来通知。
 */
export function openBluetoothAdapter(options) {
    // UTSBLE.openBluetoothAdapter(options);
    return proxyApiOptions("openBluetoothAdapter", options);
}

/**
 * 关闭蓝牙适配器的异步方法。
 * 
 * @param {CloseBluetoothAdapterOptions} options - 关闭蓝牙适配器的配置选项。
 * 
 * @returns {void} - 此函数没有直接返回值，其操作结果通过在`CloseBluetoothAdapterOptions`中定义的回调函数传达。
 */
export function closeBluetoothAdapter(options) {
    // UTSBLE.closeBluetoothAdapter(options);
    return proxyApiOptions("closeBluetoothAdapter", options);
}

/**
 * 异步获取蓝牙适配器当前状态的方法。
 * 
 * @param {GetBluetoothAdapterStateOptions} options - 获取蓝牙适配器状态的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，而是通过在`GetBluetoothAdapterStateOptions`中定义的回调函数来传递结果。
 */
export function getBluetoothAdapterState(options) {
    // UTSBLE.getBluetoothAdapterState(options);
    return proxyApiOptions("getBluetoothAdapterState", options);
}

/**
 * 异步启动蓝牙设备发现过程的方法。
 * 
 * @param {StartBluetoothDevicesDiscoveryOptions} options - 蓝牙设备发现的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，发现设备的过程和结果通过在`StartBluetoothDevicesDiscoveryOptions`中指定的回调函数来通知。
 */
export function startBluetoothDevicesDiscovery(options) {
    // UTSBLE.startBluetoothDevicesDiscovery(options);
    return proxyApiOptions("startBluetoothDevicesDiscovery", options);
}

/**
 * 异步获取已发现蓝牙设备列表的方法。
 * 
 * @param {GetBluetoothDevicesOptions} options - 获取蓝牙设备列表的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，而是通过在`GetBluetoothDevicesOptions`中定义的回调函数来传递获取到的设备列表信息。
 */
export function getBluetoothDevices(options) {
    // UTSBLE.getBluetoothDevices(options);
    return proxyApiOptions("getBluetoothDevices", options);
}

/**
 * 异步停止正在进行的蓝牙设备发现过程的方法。
 * 
 * @param {StopBluetoothDevicesDiscoveryOptions} options - 停止蓝牙设备发现的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，停止操作的结果通过在`StopBluetoothDevicesDiscoveryOptions`中定义的回调函数来通知。
 */
export function stopBluetoothDevicesDiscovery(options) {
    // UTSBLE.stopBluetoothDevicesDiscovery(options);
    return proxyApiOptions("stopBluetoothDevicesDiscovery", options);
}

/**
 * 异步创建与蓝牙低功耗（BLE）设备的连接方法。
 * 
 * @param {CreateBLEConnectionOptions} options - 创建连接的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，连接的结果通过在`CreateBLEConnectionOptions`中定义的回调函数来通知。
 */
export function createBLEConnection(options) {
    // UTSBLE.createBLEConnection(options);
    return proxyApiOptions("createBLEConnection", options);
}

/**
 * 异步关闭与蓝牙低功耗（BLE）设备的连接方法。
 * 
 * @param {CloseBLEConnectionOptions} options - 关闭连接的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，关闭连接的结果通过在`CloseBLEConnectionOptions`中定义的回调函数来通知。
 */
export function closeBLEConnection(options) {
    // UTSBLE.closeBLEConnection(options);
    return proxyApiOptions("closeBLEConnection", options);
}

/**
 * 异步设置蓝牙低功耗（BLE）设备最大传输单元（MTU）大小的方法。
 * 
 * @param {SetBLEMTUOptions} options - 设置MTU的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，设置MTU的结果通过在`SetBLEMTUOptions`中定义的回调函数来通知。
 */
export function setBLEMTU(options) {
    // UTSBLE.setBLEMTU(options);
    return proxyApiOptions("setBLEMTU", options);
}

/**
 * 异步获取蓝牙低功耗（BLE）设备最大传输单元（MTU）的方法。
 * 
 * @param {GetBLEMTUOptions} options - 获取MTU的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，获取到的MTU信息通过在`GetBLEMTUOptions`中定义的回调函数来传递。
 */
export function getBLEMTU(options) {
    // UTSBLE.getBLEMTU(options);
    return proxyApiOptions("getBLEMTU", options);
}

/**
 * 异步获取蓝牙低功耗（BLE）设备服务的方法。
 * 
 * @param {GetBLEDeviceServicesOptions} options - 获取服务的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，获取到的服务列表信息通过在`GetBLEDeviceServicesOptions`中定义的回调函数来传递。
 */
export function getBLEDeviceServices(options) {
    // UTSBLE.getBLEDeviceServices(options);
    return proxyApiOptions("getBLEDeviceServices", options);
}

/**
 * 异步获取蓝牙低功耗（BLE）设备指定服务下的所有特征值的方法。
 * 
 * @param {GetBLEDeviceCharacteristicsOptions} options - 获取特征值的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，获取到的特征值列表信息通过在`GetBLEDeviceCharacteristicsOptions`中定义的回调函数来传递。
 */
export function getBLEDeviceCharacteristics(options) {
    // UTSBLE.getBLEDeviceCharacteristics(options);
    return proxyApiOptions("getBLEDeviceCharacteristics", options);
}

/**
 * 异步读取蓝牙低功耗（BLE）设备指定特征值的方法。
 * 
 * @param {ReadBLECharacteristicValueOptions} options - 读取特征值的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，读取特征值的结果通过在`ReadBLECharacteristicValueOptions`中定义的回调函数来通知。
 */
export function readBLECharacteristicValue(options) {
    // UTSBLE.readBLECharacteristicValue(options);
    return proxyApiOptions("readBLECharacteristicValue", options);
}

/**
 * 异步写入蓝牙低功耗（BLE）设备指定特征值的方法。
 * 
 * @param {WriteBLECharacteristicValueOptions} options - 写入特征值的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，写入特征值的结果通过在`WriteBLECharacteristicValueOptions`中定义的回调函数来通知。
 */
export function writeBLECharacteristicValue(options) {
    // UTSBLE.writeBLECharacteristicValue(options);
    return proxyApiOptions("writeBLECharacteristicValue", options);
}

/**
 * 异步批量写入蓝牙低功耗（BLE）设备指定特征值的方法，支持按指定大小分包发送。
 * 
 * @param {BatchWriteBLECharacteristicValueOptions} options - 批量写入特征值的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，批量写入特征值的结果通过在`BatchWriteBLECharacteristicValueOptions`中定义的回调函数来通知。
 */
export function batchWriteBLECharacteristicValue(options) {
    // UTSBLE.batchWriteBLECharacteristicValue(options);
    return proxyApiOptions("batchWriteBLECharacteristicValue", options);
}

/**
 * 异步设置蓝牙低功耗（BLE）设备特征值通知状态的方法，可以开启或关闭特征值的通知或指示。
 * 
 * @param {NotifyBLECharacteristicValueChangeOptions} options - 设置特征值通知状态的配置选项。
 * 
 * @returns {void} - 此函数不直接返回值，设置通知状态的结果通过在`NotifyBLECharacteristicValueChangeOptions`中定义的回调函数来通知。
 */
export function notifyBLECharacteristicValueChange(options) {
    // UTSBLE.notifyBLECharacteristicValueChange(options);
    return proxyApiOptions("notifyBLECharacteristicValueChange", options);
}

// ** 

/**
 * 为了解决UTS插件在UniappJS引擎上的编译器导致 callback 每次都不唯一而导致重复注册callback的问题，
 *  我们需要暂时封装的使用替代方案来解决，替代方案的接口名称结尾为 UTSCallback
 *  UTSCallback 的接口只应该在Uniapp JS引擎中作为替代方案使用，在X上，你无法使用本BLE.js，也不要调用 UTSCallback 结尾的接口
 *   在问题 https://issues.dcloud.net.cn/pages/issues/detail?id=2214 解决后，插件开发者应当恢复正常用法，减少代码复杂度
 * @param {string} apiName 接口名称
 * @param {Object} callback 传给接口的callback函数引用
 */
function uniqueCallbackWorkaround(apiName, callback) {
    UTSBLE[`${apiName}UTSCallback`](callback);
}

/**
 * 注册蓝牙低功耗（BLE）设备连接状态变化监听的函数。
 * 
 * @param {OnBLEConnectionStateChangeCallback} callback - 当蓝牙设备的连接状态发生改变时调用的回调函数。
 * 
 * @returns {void} - 此函数不返回值，通过提供的回调函数来通知BLE设备连接状态的变化。
 */
export function onBLEConnectionStateChange(callback) {
    // UTSBLE.onBLEConnectionStateChange(callback);
    uniqueCallbackWorkaround('onBLEConnectionStateChange', callback);
}

/**
 * 移除蓝牙低功耗（BLE）设备连接状态变化监听的函数。
 * 如果未提供回调函数，则移除所有对该事件的监听。
 * 
 * @param {OnBLEConnectionStateChangeCallback=} callback - 可选参数，要移除的特定回调函数。如果不提供，则移除所有对该事件的监听器。
 * 
 * @returns {void} - 此函数不返回值，用于取消注册的BLE设备连接状态变化监听。
 */
export function offBLEConnectionStateChange(callback) {
    // UTSBLE.offBLEConnectionStateChange(callback);
    uniqueCallbackWorkaround('offBLEConnectionStateChange', callback);
}

/**
 * 注册蓝牙低功耗（BLE）设备最大传输单元（MTU）变化监听的函数。
 * 
 * @param {OnBLEMTUChangeCallback} callback - 当蓝牙设备的MTU值发生改变时调用的回调函数。
 * 
 * @returns {void} - 此函数不返回值，通过提供的回调函数来通知BLE设备MTU值的变化。
 */
export function onBLEMTUChange(callback) {
    // UTSBLE.onBLEMTUChange(callback);
    uniqueCallbackWorkaround('onBLEMTUChange', callback);
}

/**
 * 移除蓝牙低功耗（BLE）设备最大传输单元（MTU）变化监听的函数。
 * 如果未提供回调函数，则移除所有对该事件的监听。
 * 
 * @param {OnBLEMTUChangeCallback=} callback - 可选参数，要移除的特定MTU变化监听的回调函数。如果不提供，则移除所有对该事件的监听器。
 * 
 * @returns {void} - 此函数不返回值，用于取消注册的BLE设备MTU变化监听。
 */
export function offBLEMTUChange(callback) {
    // UTSBLE.offBLEMTUChange(callback);
    uniqueCallbackWorkaround('offBLEMTUChange', callback);
}

// 以下事件监听注册函数为非列表缓存型注册，可忽略封装

/**
 * 注册蓝牙设备发现事件监听的函数。
 * 当有新的蓝牙设备被找到时，会触发注册的回调函数。
 * 
 * @param {OnBluetoothDeviceFoundCallback} callback - 设备发现回调函数，传入发现的设备列表作为参数。
 * 
 * @returns {void} - 此函数不返回值，通过回调函数响应蓝牙设备的发现事件。
 */
export function onBluetoothDeviceFound(callback) {
    UTSBLE.onBluetoothDeviceFound(callback);
}

/**
 * 移除蓝牙设备发现事件监听的函数。
 * 调用后，将不再接收新的蓝牙设备发现事件。
 * 
 * @returns {void} - 此函数不返回值，用于取消已注册的蓝牙设备发现事件监听。
 */
export function offBluetoothDeviceFound() {
    UTSBLE.offBluetoothDeviceFound();
}

/**
 * 注册蓝牙适配器状态变化监听的函数。
 * 
 * @param {OnBluetoothAdapterStateChangeCallback} callback - 当蓝牙适配器状态发生变化时被调用的回调函数。
 * 
 * @returns {void} - 此函数不返回值，通过回调函数`OnBluetoothAdapterStateChangeCallback`来响应蓝牙适配器状态的变化。
 */
export function onBluetoothAdapterStateChange(callback) {
    UTSBLE.onBluetoothAdapterStateChange(callback);
}

/**
 * 移除蓝牙适配器状态变化监听的函数。
 * 
 * @returns {void} - 此函数不返回值，用于取消之前注册的蓝牙适配器状态变化事件监听。
 */
export function offBluetoothAdapterStateChange() {
    UTSBLE.offBluetoothAdapterStateChange();
}

/**
 * 注册蓝牙低功耗（BLE）特征值变化监听的函数。
 * 
 * @param {OnBLECharacteristicValueChangeCallback} callback - 当特征值发生变化时调用的回调函数。
 * 
 * @returns {void} - 此函数不返回值，特征值变化的通知通过提供的回调函数来传递。
 */
export function onBLECharacteristicValueChange(callback) {
    UTSBLE.onBLECharacteristicValueChange(callback);
}

/**
 * 移除蓝牙低功耗（BLE）特征值变化监听的函数。
 * 
 * @returns {void} - 此函数不返回值，用于取消已注册的特征值变化监听。
 */
export function offBLECharacteristicValueChange() {
    UTSBLE.offBLECharacteristicValueChange();
}

// **

// ------------------------ 二次封装的接口定义 ------------------------
